[SwiftUI] Canvasを使ってグラデーションを描画。DragGestureで動かす
こんにちは。きんくまです。SwiiftUI勉強中です。
今回作ったもの
Canvas上でドラッグすると、グラデーションの中心点が移動します
ソースコード
struct ContentView: View {
@State private var dragPoint: CGPoint = .zero
private let canvasPadding: CGFloat = 30
private let dragPointRadius: CGFloat = 10
private var dragPointDiameter: CGFloat {
dragPointRadius * 2
}
func dragFunc(canvasSize: CGSize) -> some Gesture {
DragGesture()
.onChanged { value in
// 中心点がCavnvasの範囲内におさまるようにする
var newLocation = value.location
if newLocation.x < 0 {
newLocation.x = 0
}
if newLocation.x > canvasSize.width {
newLocation.x = canvasSize.width
}
if newLocation.y < 0 {
newLocation.y = 0
}
if newLocation.y > canvasSize.height {
newLocation.y = canvasSize.height
}
dragPoint = CGPoint(x: newLocation.x, y: newLocation.y)
}
.onEnded { value in
}
}
var body: some View {
VStack {
GeometryReader { proxy in
Canvas { context, size in
// グラデーション
context.fill(
Path(roundedRect: .init(x: 0, y: 0, width: size.width, height: size.height), cornerRadius: 0),
with: .radialGradient(
Gradient(stops: [
.init(color: .red, location: 0),
.init(color: .orange, location: 0.4),
.init(color: .yellow, location: 0.49),
.init(color: .white, location: 0.5),
.init(color: .yellow, location: 0.51),
.init(color: .black, location: 1)
]), center: .init(x: dragPoint.x, y: dragPoint.y), startRadius: 0, endRadius: size.height / 4 + dragPoint.y * 0.6)
)
// 中心点の描画
context.fill(
Path(ellipseIn: .init(origin: CGPoint(x: dragPoint.x - dragPointRadius, y: dragPoint.y - dragPointRadius), size: .init(width: dragPointDiameter, height: dragPointDiameter))),
with: .color(.white)
)
context.stroke(
Path(ellipseIn: .init(origin: CGPoint(x: dragPoint.x - dragPointRadius, y: dragPoint.y - dragPointRadius), size: .init(width: dragPointDiameter, height: dragPointDiameter))),
with: .color(.gray), style: .init(lineWidth: 1))
}
}
.gesture(dragFunc(canvasSize: proxy.size))
}.padding(canvasPadding)
}
}
ドラッグする
.gestureモディファイアーでドラッグイベントを受け取ります。
内部でCanvasサイズを知りたかったので、GeometryReaderでCanvasを囲みました
.gesture(dragFunc(canvasSize: proxy.size))
ドラッグ中は、中心点をCanvas外に出さないようにxとy座標をおさまるようにしています
func dragFunc(canvasSize: CGSize) -> some Gesture {
DragGesture()
.onChanged { value in
// 中心点がCavnvasの範囲内におさまるようにする
var newLocation = value.location
if newLocation.x < 0 {
newLocation.x = 0
}
//以下略
グラデーション
Canvasではなく通常のShape系でグラデーションの方法は解説があったのですが、Canvasでのグラデーション方法が見つからなかったので、仕様書をみつつ、うまくいくまでビルドエラーと向き合いました
仕様書
せっかくなので下の方にドラッグするとグラデーションが大きくなるようにしてみました
何でCanvas使うの?
リアルタイムに描画処理が更新される場合、負荷が高まるだろうなという想定です。
通常の図形で描画するよりも、Canvasの方が負荷が低くなりそうだったので、Canvasを使いました
感想
昔はこういうのをたくさん書いていたので、非常に懐かしく楽しかったです